package stone926.mods.more_enchantments.mixins;

import net.minecraft.block.BlockState;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.*;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeInstance;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.damage.EntityDamageSource;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.projectile.TridentEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.SpawnEggItem;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.damage.StoneDamageSource;
import stone926.mods.more_enchantments.damage.StoneEntityDamageSource;
import stone926.mods.more_enchantments.enchantments.BombEnchantment;
import stone926.mods.more_enchantments.enchantments.ExperienceLootingEnchantment;
import stone926.mods.more_enchantments.enchantments.SolidifyWalkerEnchantment;
import stone926.mods.more_enchantments.enchantments.legendary.*;
import stone926.mods.more_enchantments.enchantments.negative.BreakLegEnchantment;
import stone926.mods.more_enchantments.enchantments.negative.ExplosionCreatorEnchantment;
import stone926.mods.more_enchantments.interfaces.*;
import stone926.mods.more_enchantments.mixins.accessors.TridentAccessor;
import stone926.mods.more_enchantments.util.DamageUtil;
import stone926.mods.more_enchantments.util.EnchantmentUtil;
import stone926.mods.more_enchantments.util.RandomUtil;

import java.util.Random;
import java.util.UUID;

import static stone926.mods.more_enchantments.nbt.CustomNbtKeys.LIVING_ENTITY.*;

@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity implements ILivingEntity, IBlockCooldown, IFateCooldown {

  @Shadow
  @Final
  private DefaultedList<ItemStack> syncedArmorStacks;
  private boolean dropsNothing = false;
  private int healedByCornelCooldown = 0;
  private int fateCooldown = 0;
  private int blockCooldown = 80;
  private int tpSuppression = 0;
  private UUID vampire = null;
  @Unique private static final UUID STABLE_KNOCKBACK_RESISTANCE_ID = UUID.fromString("1ecf83dd-4714-9734-b94a-d8b06b2ec2df");

  public LivingEntityMixin(EntityType<?> type, World world) {
    super(type, world);
  }

  @Shadow
  public abstract LivingEntity getAttacker();

  @Shadow
  public abstract void kill();

  @Shadow
  public abstract boolean isDead();

  @Shadow
  public abstract Random getRandom();

  @Shadow
  public abstract ItemStack getEquippedStack(EquipmentSlot slot);

  @Shadow
  public abstract boolean damage(DamageSource source, float amount);

  @Shadow
  protected abstract void fall(double heightDifference, boolean onGround, BlockState landedState, BlockPos landedPosition);

  @Shadow
  public abstract void readCustomDataFromNbt(NbtCompound nbt);

  @Shadow
  protected abstract void damageArmor(DamageSource source, float amount);

  @Shadow
  public abstract void writeCustomDataToNbt(NbtCompound nbt);

  @Shadow
  public abstract float getMaxHealth();

  @Shadow
  public abstract int getArmor();

  @Shadow
  @Nullable
  public abstract EntityAttributeInstance getAttributeInstance(EntityAttribute attribute);

  @Shadow public abstract Identifier getLootTable();

  @Override
  public boolean dropsNothing() {
    return dropsNothing;
  }

  @Override
  public void setDropsNothing(boolean dropsNothing) {
    this.dropsNothing = dropsNothing;
  }

  @Override
  public int getHealedByCornelCooldown() {
    return healedByCornelCooldown;
  }

  @Override
  public void setHealedByCornelCooldown(int healedByCornelCooldown) { this.healedByCornelCooldown = healedByCornelCooldown; }

  @Override
  public int getFateCooldown() {
    return fateCooldown;
  }

  @Override
  public void setFateCooldown(int fateCooldown) {
    this.fateCooldown = fateCooldown;
  }

  @Override
  public int getBlockCooldown() {
    return blockCooldown;
  }

  @Override
  public void setBlockCooldown(int blockCooldown) {
    this.blockCooldown = blockCooldown;
  }

  @Override
  public boolean canHealedByCornel() {
    return getHealedByCornelCooldown() <= 0;
  }

  @Override
  public int getTpSuppression() {
    return tpSuppression;
  }

  @Override
  public void setTpSuppression(int ticks) {
    tpSuppression = ticks;
  }

  @Nullable
  @Override
  public UUID getVampire() {
    return vampire;
  }

  @Override
  public void setVampire(UUID uuid) {
    this.vampire = uuid;
  }

  @Override
  public void removeVampire() {
    setVampire(getUuid());
  }

  @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
  private void writeCustomDataToNbt1(NbtCompound nbt, CallbackInfo ci) {
    nbt.putBoolean(DROPS_NOTHING, dropsNothing());
    nbt.putInt(HEALED_BY_CORNEL_COOLDOWN, getHealedByCornelCooldown());
    nbt.putInt(BLOCK_COOLDOWN, getBlockCooldown());
    nbt.putInt(FATE_COOLDOWN, getFateCooldown());
    nbt.putInt(TP_SUPPRESSION, getTpSuppression());
    if (getVampire() != null) { nbt.putUuid(VAMPIRE, getVampire()); }
  }

  @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
  private void readCustomDataFromNbt1(NbtCompound nbt, CallbackInfo ci) {
    if (nbt.contains(DROPS_NOTHING)) setDropsNothing(nbt.getBoolean(DROPS_NOTHING));
    if (nbt.contains(HEALED_BY_CORNEL_COOLDOWN)) setHealedByCornelCooldown(nbt.getInt(HEALED_BY_CORNEL_COOLDOWN));
    if (nbt.contains(BLOCK_COOLDOWN)) setBlockCooldown(nbt.getInt(BLOCK_COOLDOWN));
    if (nbt.contains(FATE_COOLDOWN)) setFateCooldown(nbt.getInt(FATE_COOLDOWN));
    if (nbt.contains(TP_SUPPRESSION)) setTpSuppression(nbt.getInt(TP_SUPPRESSION));
    if (nbt.contains(VAMPIRE)) setVampire(nbt.getUuid(VAMPIRE));
  }

  @Inject(method = "tick", at = @At("RETURN"))
  private void initVampire(CallbackInfo ci) {
    if (getVampire() == null) setVampire(getUuid());
  }

  @ModifyVariable(method = "damage", at = @At("HEAD"), argsOnly = true, ordinal = 0)
  private float handlePurgeProtection(float amount, DamageSource source) {
    if (!source.isOutOfWorld()
      && !DamageUtil.isDecapitation(source)
      && !DamageUtil.isDeath(source)
      && !DamageUtil.isExtinction(source)
      && !source.equals(StoneDamageSource.CORNEL)
    ) {
      int purgeLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.PURGE, (LivingEntity) (Object) this);
      amount *= PurgeEnchantment.getDamageLeftMultiplier(purgeLvl);
    }
    return amount;
  }

  @Inject(method = "damage", at = @At("HEAD"), cancellable = true)
  public void handleRetaliation(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
    Entity attacker = source.getAttacker();
    int retaliationLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.RETALIATION, (LivingEntity) (Object) this);
    if (!source.isUnblockable()
      && retaliationLvl > 0
      && attacker != null
      && !DamageUtil.isRetaliation(source)
      && !DamageUtil.isDecapitation(source)
      && RandomUtil.isPossible(RetaliationEnchantment.getRetaliationChance(retaliationLvl), random)
    ) {
      attacker.damage(StoneEntityDamageSource.retaliation(this), amount);
      cir.setReturnValue(false);
      if (world instanceof ServerWorld server) {
        server.spawnParticles(
          ParticleTypes.PORTAL, getX(), getY() + 0.2, getZ(), 100, 0.3, 0.5, 0.3, 0.22
        );
      }
    }
  }

  @Inject(method = "damage", at = @At("RETURN"), cancellable = true)
  public void handleBomb(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
    if (isDead()
      && cir.getReturnValueZ()
      && !StoneDamageSource.CORNEL.equals(source)
      && !DamageUtil.isDecapitation(source)
    ) {
      int cnt = 0;
      int maxLvl = 0;
      for (ItemStack is : syncedArmorStacks) {
        int lvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.BOMB, is);
        if (lvl > 0) {
          cnt++;
          if (lvl > maxLvl) maxLvl = lvl;
        }
      }
      if (cnt > 0) {
        LivingEntity e = (LivingEntity) (Object) this;
        e.getEntityWorld().createExplosion(
          e, e.getX(), e.getY(), e.getZ(),
          BombEnchantment.getExplosionPower(maxLvl, cnt),
          Explosion.DestructionType.NONE
        );
      }
    }
  }

  @Inject(method = "drop", at = @At("HEAD"), cancellable = true)
  private void dropSpawnEgg(DamageSource source, CallbackInfo ci) {
    Entity e = source.getAttacker();
    if (e instanceof LivingEntity l
      && EnchantmentUtil.hasEquipmentEnchantment(MoreEnchantmentsMod.SOUL_EXTRACTION, l)
      && !EnchantmentUtil.hasEquipmentEnchantment(MoreEnchantmentsMod.DESTROY_CURSE, l)
    ) {
      ItemStack spawnEggStack = new ItemStack(SpawnEggItem.forEntity(getType()));
      NbtCompound selfNbt = new NbtCompound();
      writeCustomDataToNbt(selfNbt);
      selfNbt.putFloat("Health", getMaxHealth());
      selfNbt.putShort("HurtTime", (short) 0);
      selfNbt.putInt("HurtByTimestamp", 0);
      selfNbt.putShort("DeathTime", (short) 0);
      selfNbt.putFloat("AbsorptionAmount", 0);
      selfNbt.putInt(MoreEnchantmentsMod.addIdPrefix("healedByCornelCooldown"), 0);
      selfNbt.putInt(MoreEnchantmentsMod.addIdPrefix("blockCooldown"), 0);
      selfNbt.putInt(MoreEnchantmentsMod.addIdPrefix("fateCooldown"), 0);
      selfNbt.remove("ActiveEffects");
      spawnEggStack.getOrCreateNbt().put("EntityTag", selfNbt);
      for (int i = 0; i < EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.SOUL_EXTRACTION, l); i++) {
        world.spawnEntity(new ItemEntity(world, getX(), getY(), getZ(), spawnEggStack));
      }
      ci.cancel();
    }
  }

  @Inject(method = "drop", at = @At("HEAD"), cancellable = true)
  public void drop(DamageSource source, CallbackInfo ci) {
    if (!this.world.isClient) {
      if (dropsNothing()) {
        ci.cancel();
        return;
      }
      LivingEntity attacker = getAttacker();
      if (attacker == null) return;
      if (EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.DESTROY_CURSE, attacker) > 0) {
        ci.cancel();
        return;
      }
      int experienceLootingLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.EXPERIENCE_LOOTING, attacker);
      if (source.isProjectile() && source.getSource() instanceof TridentEntity trident) {
        experienceLootingLvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.EXPERIENCE_LOOTING, ((TridentAccessor) trident).invokeAsItemStack());
      }
      if (experienceLootingLvl > 0 && RandomUtil.isPossible(ExperienceLootingEnchantment.getExperienceDropProbability(experienceLootingLvl), getRandom())) {
        ExperienceOrbEntity.spawn((ServerWorld) world, getPos(), ExperienceLootingEnchantment.getExperienceDropAmount(experienceLootingLvl, getRandom()));
      }
    }
  }

  @Inject(method = "addStatusEffect(Lnet/minecraft/entity/effect/StatusEffectInstance;Lnet/minecraft/entity/Entity;)Z", at = @At("HEAD"), cancellable = true)
  private void addStatusEffect(StatusEffectInstance effect, Entity source, CallbackInfoReturnable<Boolean> cir) {
    LivingEntity livingThis = (LivingEntity) (Object) this;
    if (PurgeEnchantment.shouldRemoveEffect(livingThis, effect)) {
      cir.cancel();
    }
  }

  @Inject(method = "applyMovementEffects", at = @At("HEAD"))
  private void applyMovementEffects(BlockPos pos, CallbackInfo ci) {
    int solidifyWalkerLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.SOLIDIFY_WALKER, (LivingEntity) (Object) this);
    if (solidifyWalkerLvl > 0) {
      SolidifyWalkerEnchantment.solidifyLava((LivingEntity) (Object) this, world, pos, solidifyWalkerLvl);
    }
  }

  @Inject(method = "getNextAirUnderwater", at = @At("HEAD"), cancellable = true)
  private void getNextAirUnderwater(int air, CallbackInfoReturnable<Integer> cir) {
    int i = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.SUFFOCATION, (LivingEntity) (Object) this);
    cir.setReturnValue(i > 0 && this.random.nextInt(i + 1) > 0 ? Math.max(air - i, -20) : air - 1);
  }

  @ModifyVariable(method = "handleFallDamage", at = @At("STORE"))
  private int handleFallDamage(int i) {
    return i + BreakLegEnchantment.getExtraFallDamage(EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.BREAK_LEG, (LivingEntity) (Object) this));
  }

  @Inject(method = "damage", at = @At("RETURN"))
  private void handleExplosionCreator(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
    if (!cir.getReturnValueZ()) return;
    if (PurgeEnchantment.shouldPreventExplosionCreation((LivingEntity) (Object) this)) return;
    if (source.isExplosive() && !getEntityWorld().isClient) {
      int explosionCreatorLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.EXPLOSION_CREATOR, (LivingEntity) (Object) this);
      int explosionCreatorLvlSum =
        EnchantmentHelper.getLevel(MoreEnchantmentsMod.EXPLOSION_CREATOR, getEquippedStack(EquipmentSlot.HEAD)) +
          EnchantmentHelper.getLevel(MoreEnchantmentsMod.EXPLOSION_CREATOR, getEquippedStack(EquipmentSlot.CHEST)) +
          EnchantmentHelper.getLevel(MoreEnchantmentsMod.EXPLOSION_CREATOR, getEquippedStack(EquipmentSlot.LEGS)) +
          EnchantmentHelper.getLevel(MoreEnchantmentsMod.EXPLOSION_CREATOR, getEquippedStack(EquipmentSlot.FEET));
      if (explosionCreatorLvl > 0) {
        Random r = getRandom();
        for (int i = 0; i < ExplosionCreatorEnchantment.getTntCount(explosionCreatorLvl, r); i++) {
          TntEntity tnt = new TntEntity(getEntityWorld(), getX(), getY() + 3.5, getZ(), source.getAttacker() instanceof LivingEntity l ? l : (LivingEntity) (Object) this);
          int addition = 0;
          int x = Math.round(explosionCreatorLvlSum / 2F);
          if (x > 0) {
            addition += world.random.nextInt(x + 1);
          }
          ((ITNT) tnt).setPower(ExplosionCreatorEnchantment.getExplosionPower(explosionCreatorLvl + addition));
          ((ITNT) tnt).setDestructionType(ExplosionCreatorEnchantment.getDestructionType(explosionCreatorLvl));
          ((ITNT) tnt).setSummonedByExplosionCreator(true);
          tnt.addVelocity(
            r.nextInt(2) == 0 ? r.nextDouble() / 2.1 : -r.nextDouble() / 2.1,
            r.nextDouble() / 1.4 + 0.02,
            r.nextInt(2) == 0 ? r.nextDouble() / 2.1 : -r.nextDouble() / 2.1
          );
          tnt.setFuse(20 + r.nextInt(80));
          getEntityWorld().spawnEntity(tnt);
        }
      }
    }
  }

  @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
  protected void writeCustomDataToNbt2(NbtCompound nbt, CallbackInfo ci) {
    nbt.putBoolean(FROM_BLOCK_MANDALA, ((IEntitySpawnedByFlower) this).isSpawnedByMandala());
    if (this instanceof ICreeperEntity iCreeperEntity) {
      nbt.putBoolean(CREEPER_DESTRUCTION_TYPE_NONE, iCreeperEntity.destructionTypeNone());
    }
  }

  @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
  private void readCustomDataFromNbt2(NbtCompound nbt, CallbackInfo ci) {
    if (nbt.contains(FROM_BLOCK_MANDALA)) {
      ((IEntitySpawnedByFlower) this).setSpawnedByMandala(nbt.getBoolean(FROM_BLOCK_MANDALA));
    }
    if (this instanceof ICreeperEntity iCreeperEntity && nbt.contains(CREEPER_DESTRUCTION_TYPE_NONE)) {
      iCreeperEntity.setDestructionTypeNone(nbt.getBoolean(CREEPER_DESTRUCTION_TYPE_NONE));
    }
  }

  @Override
  public boolean forceDamage(DamageSource source, float amount) {
    timeUntilRegen = 0;
    ((LivingEntity) (Object) this).hurtTime = 0;
    return damage(source, amount);
  }

  @Inject(method = "tick", at = @At("HEAD"))
  public void tick(CallbackInfo ci) {
    if (this.healedByCornelCooldown > 0) healedByCornelCooldown--;
    else healedByCornelCooldown = 0;

    if (blockCooldown > 0) blockCooldown--;
    else blockCooldown = 0;

    if (fateCooldown > 0) fateCooldown--;
    else fateCooldown = 0;

    if (tpSuppression > 0) tpSuppression--;
    else tpSuppression = 0;
  }

  @Redirect(method = "takeKnockback", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;setVelocity(DDD)V"))
  private void handleStableEnchantment(LivingEntity instance, double x, double y, double z) {
    int lvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.STABLE, instance);
    if (lvl > 0) {
      Vec3d knockback = StableEnchantment.getKnockback(x, y, z, lvl);
      instance.setVelocity(knockback.x, knockback.y, knockback.z);
    } else {
      instance.setVelocity(x, y, z);
    }
  }

  @Inject(method = "tickMovement", at = @At("HEAD"))
  private void tickStable(CallbackInfo ci) {
    removeStableKnockbackResistance();
    addStableKnockbackResistanceIfNeeded();
  }

  @Unique
  private void addStableKnockbackResistanceIfNeeded() {
    int i = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.STABLE, (LivingEntity) (Object) this);
    if (i > 0) {
      EntityAttributeInstance entityAttributeInstance = getAttributeInstance(EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE);
      if (entityAttributeInstance != null) {
        entityAttributeInstance.addTemporaryModifier(new EntityAttributeModifier(
          STABLE_KNOCKBACK_RESISTANCE_ID,
          "Stable Enchantment", 0.1 + i * 0.3,
          EntityAttributeModifier.Operation.ADDITION
        ));
      }
    }
  }

  @Unique
  private void removeStableKnockbackResistance() {
    EntityAttributeInstance entityAttributeInstance = getAttributeInstance(EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE);
    if (entityAttributeInstance != null) {
      if (entityAttributeInstance.getModifier(STABLE_KNOCKBACK_RESISTANCE_ID) != null) {
        entityAttributeInstance.removeModifier(STABLE_KNOCKBACK_RESISTANCE_ID);
      }
    }
  }

  @Inject(method = "damage", at = @At("HEAD"), cancellable = true)
  private void handleBlockingEnchantment(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
    if (!world.isClient
      && blockCooldown == 0
      && !DamageUtil.isDeath(source)
      && !DamageUtil.isDecapitation(source)
      && !source.isOutOfWorld()
      && !(((IEntitySpawnedByFlower) this).isSpawnedByMandala() && source.equals(StoneDamageSource.CORNEL))
      && !(((IEntitySpawnedByFlower) this).isSpawnedByMandala() && DamageUtil.isExtinction(source))
    ) {
      int lvl = EnchantmentUtil.getTotalEquipmentLvl(MoreEnchantmentsMod.DAMAGE_BLOCKING, (LivingEntity) (Object) this);
      if (lvl > 0 && RandomUtil.isPossible(DamageBlockingEnchantment.getBlockChance(lvl), random)) {
        cir.setReturnValue(false);
        timeUntilRegen = 20;
        blockCooldown = DamageBlockingEnchantment.getBlockCooldown(EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.DAMAGE_BLOCKING, (LivingEntity) (Object) this), getRandom());
        ((ServerWorld) world).spawnParticles(
          MoreEnchantmentsMod.DAMAGE_BLOCKED,
          getX(), getY() + getEyeHeight(getPose()) / 3F, getZ(),
          55, 0.2, 0.2, 0.2, 0.1
        );
      }
    }
  }

  @Inject(method = { "applyEnchantmentsToDamage", "applyArmorToDamage" }, at = @At("HEAD"), cancellable = true)
  private void applyArmorToDamage(DamageSource source, float amount, CallbackInfoReturnable<Float> cir) {
    if (source instanceof EntityDamageSource entityDamageSource) {
      Entity att = entityDamageSource.getAttacker();
      if (att instanceof LivingEntity attacker) {
        if (EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.ARMOR_BREAKING, attacker) > 0)
          cir.setReturnValue(amount);
      }
    }
  }

  @Redirect(method = "applyArmorToDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;damageArmor(Lnet/minecraft/entity/damage/DamageSource;F)V"))
  private void breakArmor(LivingEntity victim, DamageSource source, float amount) {
    if (source instanceof EntityDamageSource entityDamageSource) {
      Entity att = entityDamageSource.getAttacker();
      if (att instanceof LivingEntity attacker) {
        int i = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.ARMOR_BREAKING, attacker);
        damageArmor(source, ArmorBreakingEnchantment.getArmorDamageAmount(i));
      }
    }
    damageArmor(source, amount);
  }

  @Redirect(method = "applyDamage", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;setHealth(F)V"))
  private void fate(LivingEntity victim, float health, DamageSource source, float amount) {
    if (fateCooldown == 0
      && health <= 0
      && !source.isOutOfWorld()
      && !DamageUtil.isDeath(source)
      && !(((IEntitySpawnedByFlower) this).isSpawnedByMandala() && source.equals(StoneDamageSource.CORNEL))
    ) {
      int i = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.FATE, victim);
      if (i > 0) {
        fateCooldown = FateEnchantment.getCooldownTicks(EnchantmentUtil.getTotalEquipmentLvl(MoreEnchantmentsMod.FATE, victim));
        if (world instanceof ServerWorld server) {
          server.spawnParticles(ParticleTypes.TOTEM_OF_UNDYING, victim.getX(), victim.getY() + victim.getEyeHeight(victim.getPose()), victim.getZ(), 50, 0.3, 0.3, 0.3, 0.4);
        }
        victim.addStatusEffect(new StatusEffectInstance(StatusEffects.RESISTANCE, RandomUtil.nextInt(60, 110, random), 6));
        victim.addStatusEffect(new StatusEffectInstance(StatusEffects.STRENGTH, RandomUtil.nextInt(50, 100, random), RandomUtil.nextInt(1, 3, random)));
        victim.addStatusEffect(new StatusEffectInstance(StatusEffects.INVISIBILITY, RandomUtil.nextInt(200, 230, random), 1));
        return;
      }
    }
    victim.setHealth(health);
  }

  @Inject(method = "teleport", at = @At("HEAD"), cancellable = true)
  private void teleport(double x, double y, double z, boolean particleEffects, CallbackInfoReturnable<Boolean> cir) {
    if (getTpSuppression() > 0) cir.setReturnValue(false);
  }

  @Inject(method = "addStatusEffect(Lnet/minecraft/entity/effect/StatusEffectInstance;Lnet/minecraft/entity/Entity;)Z", at = @At("RETURN"))
  private void addVampire(StatusEffectInstance effect, Entity source, CallbackInfoReturnable<Boolean> cir) {
    if (cir.getReturnValueZ() && source != null && effect.getEffectType().equals(MoreEnchantmentsMod.VAMPIRE_EFFECT)) {
      setVampire(source.getUuid());
    }
  }

  @Inject(method = "setStatusEffect", at = @At("RETURN"))
  private void addVampire(StatusEffectInstance effect, Entity source, CallbackInfo ci) {
    if (source != null && effect.getEffectType().equals(MoreEnchantmentsMod.VAMPIRE_EFFECT)) {
      setVampire(source.getUuid());
    }
  }

}
